#include <click/config.h>
#include <click/confparse.hh>
#include <click/error.hh>
#include "QosManager.hh"

CLICK_DECLS

#define PATHTIMERCYCLE 5000

int QosManager::status;

QosManager::QosManager() {
	TimerData* data = new TimerData(); 
	data->qosManager = this;
	pathTimer = new Timer(&QosManager::handlePathRun, data);
	pathTimer->initialize(this);
	status = 0;
}

QosManager::~QosManager() {
}

int QosManager::configure(Vector<String> &conf, ErrorHandler *errh) {
	if (cp_va_kparse(conf, this, errh, "ADDRESS", cpkM+cpkP, cpIPAddress, &fAddress, cpEnd) < 0) return -1;
	
	return 0;
}

void QosManager::handlePathRun(Timer* t, void* data) {
	TimerData* timerData = (TimerData*) data;
	
	if (status == 0){
		WritablePacket* wp = timerData->qosManager->makePathMessage();
		timerData->qosManager->output(0).push(wp);
	
		t->schedule_after_ms(PATHTIMERCYCLE);
	}
}

int QosManager::stop(const String &conf, Element *e, void * thunk, ErrorHandler * errh){
	click_chatter("Client stop sending Path messages.");
	status = 1;
}

int QosManager::resume(const String &conf, Element *e, void * thunk, ErrorHandler * errh){
	click_chatter("Client resume sending Path messages.");
	status = 0;
}

int QosManager::teardown(const String &conf, Element *e, void * thunk, ErrorHandler * errh){
	click_chatter("Client send Teardown message.");
	
	QosManager* manager = (QosManager*) e;
	
	WritablePacket* wp = manager->makeTeardownMessage();
	manager->output(0).push(wp);
	
	status = 1;
}

void QosManager::add_handlers() {
	add_write_handler("stop", &stop, (void *)0);
	add_write_handler("resume", &resume, (void *)0);	
	add_write_handler("teardown", &teardown, (void *)0);	
}

void QosManager::push(int value, Packet *p) {
	WritablePacket* q = p->uniqueify();
    click_ip* ipHeader = reinterpret_cast<click_ip *>(q->data());
	
	if (ipHeader->ip_p == IP_PROTO_RSVP) {
		RsvpHeader* rsvpHeader = (RsvpHeader*) (ipHeader+1);
		if (rsvpHeader->type == 1) {
			click_chatter("Client got Path message.");
			in_addr source = ipHeader->ip_src;
			WritablePacket* wp = makeResvMessage(source);
			output(0).push(wp);
		} else if (rsvpHeader->type == 2) {
			click_chatter("Client got Resv message.");
		} else if (rsvpHeader->type == 5) {
			click_chatter("Client got Teardown message.");
		}
	} else {
		if (!pathTimer->scheduled()) {
			pathTimer->schedule_after_ms(1);
		}
		output(1).push(p);
	}	
}

WritablePacket* QosManager::makeTeardownMessage() {
	
	int tailroom = 0;
	int packetsize = sizeof(click_ip) + sizeof(RsvpHeader) + sizeof(TearDownData);
	int headroom =  sizeof(click_ether);
	WritablePacket* packet = Packet::make(headroom, 0, packetsize, tailroom);
	
	if (packet == 0) {
		click_chatter("Failed to make path message.");
	}
	
	memset(packet->data(), 0, packet->length());
	
	// IP header
	
	click_ip* ipHeader = reinterpret_cast<click_ip*>(packet->data());
	
	ipHeader->ip_v = 4;
	ipHeader->ip_hl = sizeof(click_ip) >> 2;
	ipHeader->ip_tos = 192;
	ipHeader->ip_len = htons(packet->length());
	uint16_t ip_id = (0 % 0xFFFF) + 1;
	ipHeader->ip_id = htons(ip_id);
	ipHeader->ip_off = 0;
	ipHeader->ip_p = IP_PROTO_RSVP;
	ipHeader->ip_ttl = 10;
	ipHeader->ip_src = fAddress;
	ipHeader->ip_dst = IPAddress("192.168.2.1");
	ipHeader->ip_sum = click_in_cksum((unsigned char*) ipHeader, sizeof(click_ip));
	packet->set_dst_ip_anno(IPAddress("192.168.2.1"));
	packet->set_ip_header(ipHeader, sizeof(click_ip));
	packet->timestamp_anno().set_now();
	
	RsvpHeader* pathHeader = (RsvpHeader*) (ipHeader+1);
	pathHeader->versionflags = 16; // Version=1 and flags=0, binary: 00010000
	pathHeader->type = 5; 
	pathHeader->sendttl = 10;
	pathHeader->reserved = 0;
	pathHeader->length = htons(sizeof(RsvpHeader) + sizeof(TearDownData));
	
	TearDownData* pathData = (TearDownData*) (pathHeader+1);
	
	pathData->sessionLength = htons(12);
	pathData->sessionObjectclass = 1;
	pathData->sessionCtype = 1;
	pathData->destAddress = IPAddress("192.168.2.1");
	pathData->protocolId = 17;
	pathData->flags = 0;
	pathData->destPort = htons(1000);
	
	pathHeader->checksum = click_in_cksum((unsigned char *) pathHeader, sizeof(RsvpHeader) + sizeof(TearDownData));
	
	return packet;
}

WritablePacket* QosManager::makeResvMessage(in_addr destination) {
	
	int tailroom = 0;
	int packetsize = sizeof(click_ip) + sizeof(RsvpHeader) + sizeof(ResvData);
	int headroom =  sizeof(click_ether);
	WritablePacket *packet = Packet::make(headroom, 0, packetsize, tailroom);
	if (packet == 0) {
		click_chatter("Cannot make Resv message.");
	}
	memset(packet->data(), 0, packet->length());
	
	click_ip* header = reinterpret_cast<click_ip*>(packet->data());
	header->ip_v = 4;
	header->ip_hl = sizeof(click_ip) >> 2;
	header->ip_tos = 192;
	header->ip_len = htons(packet->length());
	uint16_t ip_id = (0 % 0xFFFF) + 1;
	header->ip_id = htons(ip_id);
	header->ip_off = 0;
	header->ip_p = IP_PROTO_RSVP;
	header->ip_ttl = 10;
	header->ip_src = fAddress;
	header->ip_dst = destination;
	
	header->ip_sum = click_in_cksum((unsigned char*) header, sizeof(click_ip));
	
	packet->set_dst_ip_anno(destination);
	packet->set_ip_header(header, sizeof(click_ip));
	packet->timestamp_anno().set_now();
	
	RsvpHeader* rsvpHeader = (RsvpHeader*) (header+1);
	rsvpHeader->versionflags = 20;
	rsvpHeader->type = 2;
	
	rsvpHeader->reserved = 0;
	rsvpHeader->sendttl = 10;
	rsvpHeader->length = htons(sizeof(RsvpHeader) + sizeof(ResvData));
	
	ResvData* resvdata = (ResvData*) (rsvpHeader+1);
	
	resvdata->length1 = htons(12);
	resvdata->objectclass1 = 1;
	resvdata->type1 = 1;
	resvdata->destaddress = destination;
	resvdata->protocolid = 17;
	resvdata->flags = 0;
	resvdata->destport = htons(1000);
	
	resvdata->length2 = htons(8);
	resvdata->objectclass2 = 5;
	resvdata->type2 = 1;
	resvdata->refresh = htonl(3);
	
	resvdata->length3 = htons(8);
	resvdata->objectclass3 = 8;
	resvdata->type3 = 1;
	resvdata->sflags = 2;
	resvdata->sreserved = 0;
	resvdata->soptions = 0;
	
	rsvpHeader->checksum = click_in_cksum((unsigned char *)rsvpHeader, sizeof(RsvpHeader) + sizeof(ResvData));
	
	return packet;
}

WritablePacket* QosManager::makePathMessage() {
	
	int tailroom = 0;
	int packetsize = sizeof(click_ip) + sizeof(RsvpHeader) + sizeof(PathData);
	int headroom =  sizeof(click_ether);
	WritablePacket* packet = Packet::make(headroom, 0, packetsize, tailroom);
	
	if (packet == 0) {
		click_chatter("Failed to make path message.");
	}
	memset(packet->data(), 0, packet->length());
	
	// IP header
	click_ip* ipHeader = reinterpret_cast<click_ip*>(packet->data());
	ipHeader->ip_v = 4;
	ipHeader->ip_hl = sizeof(click_ip) >> 2;
	ipHeader->ip_tos = 192;
	ipHeader->ip_len = htons(packet->length());
	uint16_t ip_id = (0 % 0xFFFF) + 1;
	ipHeader->ip_id = htons(ip_id);
	ipHeader->ip_off = 0;
	ipHeader->ip_p = IP_PROTO_RSVP;
	ipHeader->ip_ttl = 10;
	ipHeader->ip_src = fAddress;
	ipHeader->ip_dst = IPAddress("192.168.2.1");
	ipHeader->ip_sum = click_in_cksum((unsigned char*) ipHeader, sizeof(click_ip));
	packet->set_dst_ip_anno(IPAddress("192.168.2.1"));
	packet->set_ip_header(ipHeader, sizeof(click_ip));
	packet->timestamp_anno().set_now();
	
	// RSVP path message header
	RsvpHeader* pathHeader = (RsvpHeader*) (ipHeader+1);
	pathHeader->versionflags = 16;		// Version=1 and flags=0, binary: 00010000
	pathHeader->type = 1;				// Path message
	
	pathHeader->sendttl = 10;
	pathHeader->reserved = 0;
	pathHeader->length = htons(sizeof(RsvpHeader) + sizeof(PathData));
	
	PathData* pathData = (PathData*) (pathHeader+1);
	
	pathData->sessionLength = htons(12);
	pathData->sessionObjectclass = 1;
	pathData->sessionCtype = 1;
	pathData->destAddress = IPAddress("192.168.2.1");
	pathData->protocolId = 17;
	pathData->flags = 0;
	pathData->destPort = htons(1000);
	
	pathData->timeLength = htons(8);
	pathData->timeObjectclass = 5;
	pathData->timeCtype = 1;
	pathData->refresh = htonl(3);
	
	pathData->templateLength = htons(12);
	pathData->templateObjectclass = 11;
	pathData->templateCtype = 1;
	pathData->srcAddress = fAddress;
	pathData->reserved = 0;
	pathData->srcPort = htons(1000);
	
	pathData->tspecLength = htons(36);
	pathData->tspecObjectclass = 12;
	pathData->tspecCtype = 2;
	
	pathData->vers = 0;
	pathData->flowspecLength = htons(7);
	pathData->senderTspecServiceNumber = 1; // 256
	pathData->dataLength = htons(6);
	pathData->parameterId = 127;
	pathData->senderTspecFlags = 0;
	pathData->senderTspecParameterLength = htons(5);
	// http://www.hep.ucl.ac.uk/~ytl/qos/rsvp_01.htm
	pathData->bucketRate = 125000;
	pathData->bucketSize = htons(1000);
	pathData->peakRate = 125000;
	pathData->minPolicedUnit = htons(24);
	pathData->maxPacketSize = htons(100);
	
	pathHeader->checksum = click_in_cksum((unsigned char *) pathHeader, sizeof(RsvpHeader) + sizeof(PathData));
	
	return packet;
}

CLICK_ENDDECLS
EXPORT_ELEMENT(QosManager)
